﻿
Imports System.Net
Imports std
Imports idcommon
Imports idcommon.apiloginResponse

Public Class idlogincontext
    Public Shared timeoutMS As Int64 = 0
    Private Shared IDLOGINCONTEXT_LOGIN_TIMEMS As Int64 = 0
    Private _serverUrl As String = Nothing
    Private _id As String = Nothing
    Private _pwd As String = Nothing
    Private _urk As String = Nothing
    Private _returnUrl As String = Nothing

    Private _loggedIn As Boolean = False
    Private _host As String = Nothing
    Private _usrk As String = Nothing

    Public Function id() As String
        Return copy(_id)
    End Function

    Public Function host() As String
        Return copy(_host)
    End Function

    Public Function userRandomKey() As String
        Return copy(_urk)
    End Function

    Public Function userSiteRandomKey() As String
        Return copy(_usrk)
    End Function

    Shared Sub New()
        assert(strstartwith(apiloginPathIdentity, network.pathSeperator))
        IDLOGINCONTEXT_LOGIN_TIMEMS = counter.registerAverageAndLastAverage("IDLOGINCONTEXT_LOGIN_TIMEMS")
        timeoutMS = secondsToMilliseconds(10)
    End Sub

    Private Function loginUrl() As String
        Dim rtn As String = Nothing
        rtn = urlUtils.appendQuery(_serverUrl + apiloginPathIdentity, query.id, _id, query.returnUrl, _returnUrl)
        Return rtn
    End Function

    Private Function urkLoginUrl() As String
        assert(Not isemptystring(_urk))
        Dim rtn As String = Nothing
        rtn = loginUrl()
        rtn = urlUtils.appendQuery(rtn, query.userRandomKey, _urk)
        Return rtn
    End Function

    Private Function pwdLoginUrl() As String
        assert(Not isemptystring(_pwd))
        Dim rtn As String = Nothing
        rtn = loginUrl()
        'if using password to login, forceUpdate the userRandomKey
        rtn = urlUtils.appendQuery(rtn, query.password, _pwd, query.forceUpdate, "1")
        Return rtn
    End Function

    Private Function parseResponseContent(ByVal content As String, ByVal urklogin As Boolean) As Boolean
        Dim hasId As Boolean = False
        Dim hasHost As Boolean = False
        Dim hasUrk As Boolean = False
        Dim hasUsrk As Boolean = False
        Dim hasReturnUrl As Boolean = False

        Dim words As vector(Of String) = Nothing
        strsepar2(content, contentSeperator, words, True)
        Dim p As pair(Of String, String) = Nothing
        p = make_pair(emptyString, emptyString)
        For i As Int64 = 0 To words.size() - 1
            If Not strseg(words(i), p.first, p.second, contentNameValueSeperator) Then
                Return False
            End If
            unescape(p.second)

            If strsame(p.first, contentNaming.id) Then
                If Not strsame(p.second, _id) Then
                    raiseError(errorHandle.errorType.warning, "id responded from idsystem is different.")
                    Return False
                End If
                hasId = True
            ElseIf strsame(p.first, contentNaming.host) Then
                copy(_host, p.second)
                hasHost = True
            ElseIf strsame(p.first, contentNaming.userRandomKey) Then
                If urklogin AndAlso Not isemptystring(_urk) AndAlso Not strsame(p.second, _urk) Then
                    'if there is a userRandomKey, this context uses it to login,
                    'so the userRandomKey is correct, and cannot be different from response.
                    raiseError(errorHandle.errorType.warning, "userRandomKey responded from idsystem is different.")
                    Return False
                End If
                copy(_urk, p.second)
                hasUrk = True
            ElseIf strsame(p.first, contentNaming.userSiteRandomKey) Then
                copy(_usrk, p.second)
                hasUsrk = True
            ElseIf strsame(p.first, contentNaming.returnUrl) Then
                If Not strsame(p.second, _returnUrl) Then
                    raiseError(errorHandle.errorType.warning, "returnUrl responded from idsystem is different.")
                    Return False
                End If
                hasReturnUrl = True
            Else
                raiseError("idsystem responded an unknown content, ", p.first, ", with value ", p.second, _
                           ", protocol changed, idlogincontext will ignore this field.")
            End If
        Next

        Return hasId AndAlso hasHost AndAlso hasUrk AndAlso hasUsrk AndAlso hasReturnUrl
    End Function

    Private Sub login(ByVal url As String, ByVal urklogin As Boolean)
        Dim response As HttpWebResponse = Nothing
        If requestHttpWeb(url, response, timeoutMS:=timeoutMS) Then
            If response.StatusCode = statusCode.success Then
                Dim content As String = Nothing
                _loggedIn = readHttpResponse(response, content, url) AndAlso
                            parseResponseContent(content, urklogin)
            Else
                response.Close()
                Select Case response.StatusCode
                    Case statusCode.inputIncomplete
                        raiseError(errorHandle.errorType.warning, _
                                   "idsystem responded inputIncomplete, ", response.StatusDescription, _
                                   ", protocol changed.")
                    Case statusCode.idNotFound
                        raiseError(errorHandle.errorType.information, _
                                   "idsystem responded idNotFound, ", response.StatusDescription)
                    Case statusCode.passwordError
                        raiseError(errorHandle.errorType.information, _
                                   "idsystem responded passwordError, ", response.StatusDescription)
                    Case Else
                        raiseError(errorHandle.errorType.warning, _
                                   "idsystem responsed an unknown status code, ", response.StatusCode, ", ", _
                                   response.StatusDescription, ", protocol changed.")
                End Select
                _loggedIn = False
            End If
        Else
            raiseError(errorHandle.errorType.warning, "cannot connected to idsystem, url ", url)
            _loggedIn = False
        End If
    End Sub

    Private Sub urklogin()
        If Not isemptystring(userRandomKey()) Then
            login(urkLoginUrl(), True)
        End If
    End Sub

    Private Sub pwdlogin()
        If Not isemptystring(_pwd) Then
            login(pwdLoginUrl(), False)
        End If
    End Sub

    Private Sub _login()
        Dim startticks As Int64 = 0
        startticks = Now.Ticks()
        urklogin()
        If Not loggedIn() Then
            pwdlogin()
        End If
        counter.increase(IDLOGINCONTEXT_LOGIN_TIMEMS, ticksToMilliseconds(Now.Ticks() - startticks))
    End Sub

    Public Function login() As Boolean
        If Not _loggedIn Then
            _login()
        End If
        Return _loggedIn
    End Function

    Public Function loggedIn() As Boolean
        Return _loggedIn
    End Function

    Public Sub New(ByVal serverUrl As String, _
                   ByVal id As String, ByVal pwd As String, ByVal urk As String, ByVal returnUrl As String)
        kickRight(serverUrl, network.pathSeperator)
        copy(_serverUrl, serverUrl)
        copy(_id, id)
        copy(_pwd, pwd)
        copy(_urk, urk)
        copy(_returnUrl, returnUrl)
        login()
    End Sub

    Public Sub New(ByVal id As String, ByVal pwd As String, ByVal urk As String, ByVal returnUrl As String)
        Me.New(defaultServerUrl, id, pwd, urk, returnUrl)
    End Sub

    Public Sub New(ByVal idloginctx As idlogincontext)
        Me.New(idloginctx._serverUrl, idloginctx._id, idloginctx._pwd, idloginctx._urk, idloginctx._returnUrl)
    End Sub
End Class
